﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Spanner
{
    public partial class UI
    {
        /// <summary>
        /// ForEach(xs, a) is equivalent to xs.forEach(a) in JavaScript.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="a"></param>
        /// <returns></returns>
        public static Act ForEach<T>(Expr<T[]> xs, Expr<Fn<T, Act>> a)
        {
            return JSAct("#.forEach(#)", xs, a);
        }

        /// <summary>
        /// ForEach(xs, a) is equivalent to xs.forEach(a) in JavaScript.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="a"></param>
        /// <returns></returns>
        public static Act ForEach<T>(Expr<T[]> xs, Expr<Fn<T, int, Act>> a)
        {
            return JSAct("#.forEach(#)", xs, a);
        }

        /// <summary>
        /// ForEach(xs, a) is equivalent to xs.forEach(a) in JavaScript.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="a"></param>
        /// <returns></returns>
        public static Act ForEach<T>(Expr<T[]> xs, Expr<Fn<T, int, T[], Act>> a)
        {
            return JSAct("#.forEach(#)", xs, a);
        }

        /// <summary>
        /// Push(xs, e) is equivalent to xs.push(e) in JavaScript.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        public static Act Push<T>(Var<T[]> xs, Expr<T> e)
        {
            return JSAct("#.push(#)", xs, e);
        }

        /// <summary>
        /// Push(xs, e) is equivalent to xs.push(e) in JavaScript.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        public static Act Push<T>(Param<T[]> xs, Expr<T> e)
        {
            return JSAct("#.push(#)", xs, e);
        }

        /// <summary>
        /// Push(xs, e) pushes e on to the array in the observable xs.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        public static Act Push<T>(ObsVar<T[]> xs, Expr<T> e)
        {
            return Push(Ref(xs), e);
        }

        /// <summary>
        /// Push(xs, e) pushes e on to the array in the observable referred to by xs.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        public static Act Push<T>(Expr<ObsVarRef<T[]>> xs, Expr<T> e)
        {
            //return JSAct("spanner.pushObsArray(#, #)", xs, e);
            return JSAct("#.push(#)", xs, e);
        }

        /// <summary>
        /// Reverse(xs) reverses the items in the array in xs.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <returns></returns>
        public static Act Reverse<T>(Var<T[]> xs)
        {
            return JSAct("#.reverse()", xs);
        }

        /// <summary>
        /// Reverse(xs) reverses the items in the array in xs.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <returns></returns>
        public static Act Reverse<T>(Param<T[]> xs)
        {
            return JSAct("#.reverse()", xs);
        }

        /// <summary>
        /// Reverse(xs) reverses the items in the array in the observable xs.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <returns></returns>
        public static Act Reverse<T>(ObsVar<T[]> xs)
        {
            return Reverse(Ref(xs));
        }

        /// <summary>
        /// Reverse(xs) reverses the items in the array in the observable referred to by xs.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <returns></returns>
        public static Act Reverse<T>(Expr<ObsVarRef<T[]>> xs)
        {
            //return JSAct("spanner.reverseObsArray(#)", xs);
            return JSAct("#.reverse()", xs);
        }

        /// <summary>
        /// Sort(xs, f) sorts the array in xs
        /// according to the ordering function f (f(x, y) is positive, zero, or negative if
        /// x is greater than, equal to, or less than y respectively).
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="f"></param>
        /// <returns></returns>
        public static Act Sort<T>(Var<T[]> xs, Expr<Fn<T, T, int>> f = null)
        {
            return JSAct("#.sort(#)", xs, f);
        }

        /// <summary>
        /// Sort(xs, f) sorts the array in xs
        /// according to the ordering function f (f(x, y) is positive, zero, or negative if
        /// x is greater than, equal to, or less than y respectively).
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="f"></param>
        /// <returns></returns>
        public static Act Sort<T>(Param<T[]> xs, Expr<Fn<T, T, int>> f = null)
        {
            return JSAct("#.sort(#)", xs, f);
        }

        /// <summary>
        /// Sort(xs, f) sorts the array in the observable xs
        /// according to the ordering function f (f(x, y) is positive, zero, or negative if
        /// x is greater than, equal to, or less than y respectively).
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="f"></param>
        /// <returns></returns>
        public static Act Sort<T>(ObsVar<T[]> xs, Expr<Fn<T, T, int>> f = null)
        {
            return Sort(Ref(xs), f);
        }

        /// <summary>
        /// Sort(xs, f) sorts the array in the observable referred to by xs
        /// according to the ordering function f (f(x, y) is positive, zero, or negative if
        /// x is greater than, equal to, or less than y respectively).
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="f"></param>
        /// <returns></returns>
        public static Act Sort<T>(Expr<ObsVarRef<T[]>> xs, Expr<Fn<T, T, int>> f = null)
        {
            //return (((object)f) == null
            //       ? JSAct("spanner.sortObsArray(#)", xs)
            //       : JSAct("spanner.sortObsArray(#, #)", xs, f)
            //       );
            return (((object)f) == null
                   ? JSAct("#.sort()", xs)
                   : JSAct("#.sort(#)", xs, f)
                   );
        }

        /// <summary>
        /// Splice(xs, begin, howMany, ys) replaces the howMany items
        /// starting at index begin in the array in xs with the items
        /// ys.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="begin"></param>
        /// <param name="howMany"></param>
        /// <param name="ys"></param>
        /// <returns></returns>
        public static Act Splice<T>(Var<T[]> xs, Expr<int> begin, Expr<int> howMany, Expr<T[]> ys)
        {
            return JSAct("[].splice.apply(#, [#, #].concat(#))", xs, begin, howMany, ys);
        }

        /// <summary>
        /// Splice(xs, begin, howMany, ys) replaces the howMany items
        /// starting at index begin in the array in xs with the items
        /// ys.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="begin"></param>
        /// <param name="howMany"></param>
        /// <param name="ys"></param>
        /// <returns></returns>
        public static Act Splice<T>(Param<T[]> xs, Expr<int> begin, Expr<int> howMany, Expr<T[]> ys)
        {
            return JSAct("[].splice.apply(#, [#, #].concat(#))", xs, begin, howMany, ys);
        }

        /// <summary>
        /// Splice(xs, begin, howMany, ys) replaces the howMany items
        /// starting at index begin in the array in the observable xs with the items
        /// ys.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="begin"></param>
        /// <param name="howMany"></param>
        /// <param name="ys"></param>
        /// <returns></returns>
        public static Act Splice<T>(ObsVar<T[]> xs, Expr<int> begin, Expr<int> howMany, Expr<T[]> ys)
        {
            return Splice(Ref(xs), begin, howMany, ys);
        }

        /// <summary>
        /// Splice(xs, begin, howMany, ys) replaces the howMany items
        /// starting at index begin in the array in the observable referred to by xs with the items
        /// ys.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="begin"></param>
        /// <param name="howMany"></param>
        /// <param name="ys"></param>
        /// <returns></returns>
        public static Act Splice<T>(Expr<ObsVarRef<T[]>> xs, Expr<int> begin, Expr<int> howMany, Expr<T[]> ys)
        {
            return JSAct("spanner.spliceObsArray(#, #, #, #)", xs, begin, howMany, ys);
        }

        /// <summary>
        /// Unshift(xs, ys) inserts the items in ys at the front of the array
        /// in xs.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="ys"></param>
        /// <returns></returns>
        public static Act Unshift<T>(Var<T[]> xs, Expr<T[]> ys)
        {
            return JSAct("[].unshift.apply(#, #)", xs, ys);
        }

        /// <summary>
        /// Unshift(xs, ys) inserts the items in ys at the front of the array
        /// in xs.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="ys"></param>
        /// <returns></returns>
        public static Act Unshift<T>(Param<T[]> xs, Expr<T[]> ys)
        {
            return JSAct("[].unshift.apply(#, #)", xs, ys);
        }

        /// <summary>
        /// Unshift(xs, ys) inserts the items in ys at the front of the array
        /// in the observable xs.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="ys"></param>
        /// <returns></returns>
        public static Act Unshift<T>(ObsVar<T[]> xs, Expr<T[]> ys)
        {
            return Unshift(Ref(xs), ys);
        }

        /// <summary>
        /// Unshift(xs, ys) inserts the items in ys at the front of the array
        /// in the observable referred to by xs.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xs"></param>
        /// <param name="ys"></param>
        /// <returns></returns>
        public static Act Unshift<T>(Expr<ObsVarRef<T[]>> xs, Expr<T[]> ys)
        {
            return JSAct("spanner.unshiftObsArray(#, #)", xs, ys);
        }


    }
}
